package com.example.demo.controller;

import com.example.demo.auth.Permission;
import com.example.demo.dto.ErrorCode;
import com.example.demo.dto.ResponseValue;
import com.example.demo.dto.common.*;
import com.example.demo.dto.input.CommonInput;
import com.example.demo.entity.Consultant;
import com.example.demo.entity.User;
import com.example.demo.entity.relation.ConsultRecord;
import com.example.demo.enums.Role;
import com.example.demo.exception.EntityAlreadyExistException;
import com.example.demo.exception.EntityNotExistException;
import com.example.demo.exception.IMIDHadCreatedException;
import com.example.demo.exception.IMIDNotExistException;
import com.example.demo.redis.RedisKey;
import com.example.demo.service.AliService;
import com.example.demo.service.ConsultantService;
import com.example.demo.service.IMService;
import com.example.demo.service.UserService;
import com.example.demo.utils.IdGenerator;
import com.example.demo.utils.InputModelUtil;
import org.bouncycastle.cert.ocsp.Req;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.*;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.TimeUnit;

@RestController
@RequestMapping(value = "/consultant")
public class ConsultantController {
    private final ConsultantService consultantService;
    private final UserService userService;
    private final RedisTemplate<String, Object> redisTemplate;
    private final AliService aliService;
    private final IMService imService;
    private final IdGenerator idGenerator;

    public ConsultantController(ConsultantService consultantService,
                                UserService userService,
                                RedisTemplate<String, Object> redisTemplate,
                                AliService aliService,
                                IMService imService,
                                IdGenerator idGenerator) {
        this.consultantService = consultantService;
        this.userService = userService;
        this.redisTemplate = redisTemplate;
        this.aliService = aliService;
        this.imService = imService;
        this.idGenerator = idGenerator;
    }

    /**
     * 咨询师或督导登录
     * @param input
     * @return ResponseValue(id)
     */
    @RequestMapping(method = RequestMethod.POST, value = "/login")
    public ResponseValue<?> login(@RequestBody CommonInput input){
        Optional<CommonInput> optionalInput = Optional.of(input);
        String phoneNumber = optionalInput.map(CommonInput::getConsultant)
                                          .map(ConsultantModel::getPhoneNum)
                                          .orElse("");
        String password = optionalInput.map(CommonInput::getConsultant)
                                        .map(ConsultantModel::getPassword)
                                        .orElse("");

        if (phoneNumber.length() != 11 || password.equals("")) {
            return ResponseValue.fail(ErrorCode.REQUEST_PARAM_ERROR);
        }

        Consultant consultant = null;
        try {
            consultant = consultantService.getConsultantByPhoneNumber(phoneNumber);
        } catch (EntityNotExistException e) {
            return ResponseValue.fail(ErrorCode.REQUEST_PARAM_ERROR);
        }

        if (!password.equals(consultant.getPassword())) {
            return ResponseValue.fail(ErrorCode.REQUEST_PARAM_ERROR);
        } else {
            String redisKey = RedisKey.CONSULTANT_QUEUE.replace("{id}", consultant.getId());
            int queueCount = 0;
            redisTemplate.opsForValue().setIfAbsent(redisKey, queueCount, 5, TimeUnit.MINUTES);
            redisTemplate.opsForSet().add(RedisKey.CONSULTANT_ONLINE_SET, consultant.getId());

            ConsultantModel model = InputModelUtil.transToModelOfConsultant(consultant);

            try {
                if (imService.isOriginIdUsed(consultant.getId())) {
                    model.setImId(imService.queryIMId(consultant.getId()));
                } else {
                    String imId = imService.generateIMId(consultant.getId());
                    model.setImId(imId);
                }
                model.setImSig(imService.getIMToken(consultant.getId()));

            }catch (Exception e){
                e.printStackTrace();
            }
            if("true".equals(consultant.getIsSupervisor())){
                model.setIsSupervise("true");
            }else{
                model.setIsSupervise("false");
            }
            CommonInput output = new CommonInput();
            output.setConsultant(model);
            return ResponseValue.success(output);
        }
    }

    @RequestMapping(method = RequestMethod.POST, value = "/info")
    public ResponseValue<?> getConsultantInfo(@RequestBody CommonInput input){
        Optional<CommonInput> optionalInput = Optional.of(input);

        String consultantId = optionalInput.map(CommonInput::getConsultant)
                                            .map(ConsultantModel::getId)
                                            .orElse("");

        String userId = optionalInput.map(CommonInput::getUser)
                                     .map(UserModel::getUserId)
                                     .orElse("");

        if (consultantId.equals("")){
            return ResponseValue.fail(ErrorCode.REQUEST_PARAM_ERROR);
        }

        Consultant consultant = null;
        try {
            consultant = consultantService.getConsultant(consultantId);
        }catch (EntityNotExistException e){
            return ResponseValue.fail(ErrorCode.ENTITY_NOT_EXIST);
        }

        if (consultant == null) {
            return ResponseValue.fail(ErrorCode.ENTITY_NOT_EXIST);
        }

        ConsultantModel model = InputModelUtil.transToModelOfConsultant(consultant);

        int queueCount = getConsultantQueueCount(consultantId);
        model.setBusy(queueCount > 5 ? "true" : "false");

        if(userId.equals("")) {
            model.setAdvised("false");
        } else {
            model.setAdvised(consultantService.userAndConsultantHadRecords(userId, consultantId)?"true":"false");
        }

        CommonInput output = new CommonInput();
        output.setConsultant(model);
        return ResponseValue.success(output);
    }

    @RequestMapping(method = RequestMethod.POST, value = "/records")
    public ResponseValue<?> getConsultRecords(@RequestBody CommonInput input){
        Optional<CommonInput> optionalInput = Optional.of(input);
        String consultant_id = optionalInput.map(CommonInput::getConsultant)
                                            .map(ConsultantModel::getId)
                                            .orElse("");
        if (consultant_id.equals("")) {
            return ResponseValue.fail(ErrorCode.REQUEST_PARAM_ERROR);
        }

        ConsultantModel consultantModel = null;
        try {
            Consultant consultant = consultantService.getConsultant(consultant_id);
            consultantModel = InputModelUtil.transToModelOfConsultant(consultant);
        }catch (EntityNotExistException e) {
            e.printStackTrace();
        }

        List<ConsultRecord> consultRecords = consultantService.getConsultRecords(consultant_id);

        List<ConsultHistoryModel> histories = new ArrayList<>();
        for (ConsultRecord record : consultRecords) {
            ConsultHistoryModel model = InputModelUtil.transToConsultHistory(record);
            model.setConsultant(consultantModel);

            UserModel userModel = new UserModel();
            userModel.setUserId(record.getUserId());

            model.setUser(userModel);
            histories.add(model);
        }

        CommonInput output = new CommonInput();
        output.setConsultant_history_list(histories);
        return ResponseValue.success(output);
    }

    //@Permission(roles = {Role.ADMIN})
    @RequestMapping(method = RequestMethod.POST, value = "/admin/add")
    public ResponseValue<?> addConsultant(@RequestBody CommonInput input){
        Optional<CommonInput> optionalInput = Optional.of(input);
        String phone = optionalInput.map(CommonInput::getConsultant)
                                    .map(ConsultantModel::getPhoneNum)
                                    .orElse("");
        String password = optionalInput.map(CommonInput::getConsultant)
                                        .map(ConsultantModel::getPassword)
                                        .orElse("");
        String gender = optionalInput.map(CommonInput::getConsultant)
                                     .map(ConsultantModel::getGender)
                                     .orElse("");

        if (phone.equals("") || password.equals("")) {
            return ResponseValue.fail(ErrorCode.REQUEST_PARAM_ERROR);
        }
        String id = String.valueOf(idGenerator.nextId(Consultant.class));

        Consultant consultant = InputModelUtil.transToConsultant(input.getConsultant());
        if (consultant != null) {
            consultant.setId(id);
            consultant.setTotalMinutes(String.valueOf(0));
            consultant.setIsSupervisor("false");

            try {
                consultantService.addConsultant(consultant);
                imService.generateIMId(consultant.getId());
                return ResponseValue.success(null);
            }catch (IOException | IMIDHadCreatedException e1){
                e1.printStackTrace();
                return ResponseValue.success(null);
            }catch (EntityAlreadyExistException e2) {
                return ResponseValue.fail(ErrorCode.ENTITY_ALREADY_EXIST);
            }
        } else {
            return ResponseValue.fail(ErrorCode.REQUEST_PARAM_ERROR);
        }

    }

    //@Permission(roles = {Role.ADMIN})
    @RequestMapping(method = RequestMethod.POST, value = "/admin/delete")
    public ResponseValue<?> deleteConsultant(@RequestBody CommonInput input){
        Optional<CommonInput> optionalInput = Optional.of(input);
        String consultant_id = optionalInput.map(CommonInput::getConsultant)
                                            .map(ConsultantModel::getId)
                                            .orElse("");
        if (consultant_id.equals("")) {
            return ResponseValue.fail(ErrorCode.REQUEST_PARAM_ERROR);
        }

        try {
            Consultant consultant = consultantService.getConsultant(consultant_id);
            consultantService.deleteConsultant(consultant_id);
            return ResponseValue.success(null);

        }catch (EntityNotExistException e) {
            return ResponseValue.fail(ErrorCode.ENTITY_NOT_EXIST);
        }
    }

    //@Permission(roles = {Role.ADMIN})
    @RequestMapping(method = RequestMethod.POST, value = "/admin/update")
    public ResponseValue<?> updateConsultant(@RequestBody CommonInput input){
        Optional<CommonInput> optionalInput = Optional.of(input);
        String password = optionalInput.map(CommonInput::getConsultant)
                                        .map(ConsultantModel::getPassword)
                                        .orElse("");
        String phone = optionalInput.map(CommonInput::getConsultant)
                                    .map(ConsultantModel::getPhoneNum)
                                    .orElse("");
        String id = optionalInput.map(CommonInput::getConsultant)
                                 .map(ConsultantModel::getId)
                                 .orElse("");
        if (id.equals("")) {
            return ResponseValue.fail(ErrorCode.REQUEST_PARAM_ERROR);
        }

        Consultant consultant = InputModelUtil.transToConsultant(input.getConsultant());
        if (consultant == null) {
            return ResponseValue.fail(ErrorCode.REQUEST_PARAM_ERROR);
        }

        try {
            consultantService.updateConsultant(id, consultant);
            return ResponseValue.success(null);
        }catch (EntityNotExistException e) {
            return ResponseValue.fail(ErrorCode.ENTITY_NOT_EXIST);
        }
    }

    //@Permission(roles = {Role.ADMIN})
    @RequestMapping(method = RequestMethod.POST, value = "/admin/get")
    public ResponseValue<?> getConsultantInfoByAdmin(@RequestBody CommonInput input){
        Optional<CommonInput> optionalInput = Optional.of(input);
        String consultant_id = optionalInput.map(CommonInput::getConsultant)
                                            .map(ConsultantModel::getId)
                                            .orElse("");
        Consultant consultant = null;
        try {
            consultant = consultantService.getConsultant(consultant_id);
        } catch (EntityNotExistException e) {
            return ResponseValue.fail(ErrorCode.ENTITY_NOT_EXIST);
        }

        if (consultant == null) {
            return ResponseValue.fail(ErrorCode.ENTITY_NOT_EXIST);
        }

        ConsultantModel model = InputModelUtil.transToModelOfConsultant(consultant);

        int queueCount = getConsultantQueueCount(consultant_id);
        model.setBusy(queueCount > 5 ? "true" : "false");
        CommonInput output = new CommonInput();
        output.setConsultant(model);
        return ResponseValue.success(output);
    }

    //@Permission(roles = {Role.ADMIN})
    @RequestMapping(method = RequestMethod.POST, value = "/admin/getAll")
    public ResponseValue<?> getAllConsultantInfoByAdmin(){
        List<Consultant> consultants = consultantService.getAllConsultantByIsSupervisor(String.valueOf(false));
        List<ConsultantModel> modelList = new ArrayList<>();
        for (Consultant consultant : consultants) {
            ConsultantModel model = InputModelUtil.transToModelOfConsultant(consultant);

            int queueCount = 0;
            modelList.add(model);
        }

        CommonInput output = new CommonInput();
        output.setConsultant_list(modelList);

        return ResponseValue.success(output);
    }

    //@Permission(roles = {Role.ADMIN})
    @RequestMapping(method = RequestMethod.POST, value = "/admin/schedule")
    public ResponseValue<?> scheduleByAdmin(@RequestBody CommonInput input){
        return null;
    }

    @RequestMapping(method = RequestMethod.POST, value = "/users")
    public ResponseValue<?> getOnlineUsers() throws IOException, IMIDNotExistException, IMIDHadCreatedException {
        Set<Object> set = redisTemplate.opsForSet().members(RedisKey.USER_ONLINE_SET);
        List<String> numbers = new ArrayList<>();
        for(Object obj: set){
            if(obj!=null){
                numbers.add((String)obj);
            }
        }
        List<User> users = userService.getUserList(numbers);
        System.out.println(users.size());
        List<UserModel> models = new ArrayList<>();
        for(User user: users){
            UserModel model = new UserModel();
            model.setName(user.getName());
            model.setPhoneNum(user.getPhoneNumber());
            model.setUrgentContact(user.getUrgentName());
            model.setUrgentContactNum(user.getUrgentNumber());
            if(imService.isOriginIdUsed(user.getPhoneNumber())){
                model.setIMUserID(imService.queryIMId(user.getPhoneNumber()));
            }else{
                model.setIMUserID(imService.generateIMId(user.getPhoneNumber()));
            }
            model.setIMUserSig(imService.getIMToken(user.getPhoneNumber()));
            models.add(model);
        }
        CommonInput output = new CommonInput();
        output.setUser_list(models);
        return ResponseValue.success(output);
    }

    /**
     * 从 redis中获取当前咨询师的排队人数。
     * @param consultant_id
     * @return int 排队人数
     */
    private int getConsultantQueueCount(String consultant_id) {
        if (consultant_id == null) {
            return 0;
        }

        String redisKey = RedisKey.CONSULTANT_QUEUE.replace("{id}", consultant_id);
        Object value = redisTemplate.opsForValue().get(redisKey);
        if(value==null) return 0;
        String val = value.toString();
        if (val != null) {
            return Integer.parseInt(val);
        } else {
            return 0;
        }
    }
}
